package cn.juque.common.filters;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.juque.common.base.BaseOperator;
import cn.juque.common.constants.StringConstants;
import cn.juque.common.thread.BaseOperatorThreadLocal;
import cn.juque.common.thread.TraceLogTreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.Objects;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>消费端加载操作信息</li>
 * </ul>
 * @date 2024-03-13
 **/
@Slf4j
@Activate(group = {CommonConstants.CONSUMER}, order = 1)
public class LoadAttachmentFilter implements Filter {

    private BaseOperatorThreadLocal baseOperatorThreadLocal;

    private TraceLogTreadLocal traceLogTreadLocal;

    /**
     * Always call invoker.invoke() in the implementation to hand over the request to the next filter node.
     *
     * @param invoker    Invoker
     * @param invocation Invocation
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        log.info("try load dubbo attachment filter");
        this.initBaseOperatorThreadLocal();
        BaseOperator baseOperator = this.baseOperatorThreadLocal.get();
        this.initTraceLogTreadLocal();
        String traceLogId = this.traceLogTreadLocal.get();
        invocation.setAttachmentIfAbsent(StringConstants.BASE_OPERATOR, baseOperator);
        invocation.setAttachmentIfAbsent(StringConstants.TRACE_LOG_ID, traceLogId);
        String tenantId = CharSequenceUtil.EMPTY;
        String operatorId = CharSequenceUtil.EMPTY;
        if (Objects.nonNull(baseOperator)) {
            tenantId = baseOperator.getTenantId();
            operatorId = baseOperator.getOperatorId();
        }
        String serviceName = invocation.getServiceName();
        String methodName = invocation.getMethodName();
        log.info("dubbo load operate info: serviceName={}, methodName={}, tenantId={} operatorId={}, traceLogId={}",
                serviceName, methodName, tenantId, operatorId, traceLogId);
        return invoker.invoke(invocation);
    }

    private void initBaseOperatorThreadLocal() {
        if (Objects.isNull(this.baseOperatorThreadLocal)) {
            synchronized (this) {
                if (Objects.isNull(this.baseOperatorThreadLocal)) {
                    this.baseOperatorThreadLocal = SpringUtil.getBean(BaseOperatorThreadLocal.class);
                }
            }
        }
    }

    private void initTraceLogTreadLocal() {
        if (Objects.isNull(this.traceLogTreadLocal)) {
            synchronized (this) {
                if (Objects.isNull(this.traceLogTreadLocal)) {
                    this.traceLogTreadLocal = SpringUtil.getBean(TraceLogTreadLocal.class);
                }
            }
        }
    }
}
